import { HttpStatus, Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { DiscordSnowflake } from "@sapphire/snowflake";
import { plainToInstance } from "class-transformer";
import { DataSource, Repository } from "typeorm";
import { ResultMsg } from "@/common/enum/result-msg.enum";
import Result from "@/common/utils/result.util";
import { SysDept } from "@/database/entity/SysDept";
import { SysUserDept } from "@/database/entity/SysUserDept";
import { CreateDeptDto } from "./dto/create-dept.dto";
import { QueryDeptByNameDto } from "./dto/query-dept.dto";
import { UpdateDeptDto } from "./dto/update-dept.dto";
import { DeptDetailRawVo } from "./vo/dept-detail-raw.vo";

const DEPT_CODE_SPLIT_SYMBOL = ".";
const COLUMN_IS_LEAF = `(select not exists (select 1 from sys_dept where parent_id = dept.id) as is_leaf)`;
const DETAIL_SELECT =
    "dept.id, dept.parent_id, dept.name, dept.full_name, dept.code, dept.level, dept.type, dept.phone, dept.email, dept.remark, dept_type.name as type_name, dept_type.icon as icon";

@Injectable()
export class DeptService {
    constructor(
        @InjectRepository(SysDept)
        private departRepository: Repository<SysDept>,
        @InjectRepository(SysUserDept)
        private userDeptRepository: Repository<SysUserDept>,
        private dataSource: DataSource,
    ) {}

    /** 新增部门 */
    async create(data: CreateDeptDto) {
        const id = DiscordSnowflake.generate().toString();
        await this.dataSource.transaction(async (manager) => {
            const departRepository = manager.getRepository(SysDept);
            let parent = null;
            let fullName = data.name;

            // 计算 fullName
            if (data.parentId) {
                parent = await departRepository.findOne({ where: { id: data.parentId } });
                if (!parent) {
                    throw Result.error("The parent department was not found", HttpStatus.BAD_REQUEST);
                }
                fullName = `${parent.fullName}/${data.name}`;
            }

            // 计算 code
            const code = await getNewDepartCode(departRepository, { ...data, parentCode: parent?.code });

            const depart = departRepository.create({ ...data, id, code, fullName });
            await departRepository.insert(depart);
        });
        return Result.success(id, ResultMsg.CREATE_SUCCESS);
    }

    /** 删除部门 */
    async remove(id: string) {
        // 存在子部门时，禁止删除
        const hasChildren = await this.departRepository.exists({
            where: { parentId: id },
        });
        if (hasChildren) {
            return Result.error(ResultMsg.DELETE_HAS_CHILD);
        }

        // 当前部门有用户时，禁止删除
        const hasUser = await this.userDeptRepository.exists({
            where: { deptId: id },
        });
        if (hasUser) {
            return Result.error(ResultMsg.DELETE_HAS_USER);
        }
        const depart = await this.departRepository.findOne({ where: { id } });
        await this.departRepository.update(id, { deletedAt: new Date() });
        return Result.success(depart?.code, ResultMsg.DELETE_SUCCESS);
    }

    /** 更新部门 */
    async update(data: UpdateDeptDto) {
        const updateResult = await this.departRepository.update(data.id, { ...data });
        if (!updateResult.affected) {
            return Result.error(ResultMsg.DATA_NOT_FOUND);
        }
        return Result.success(null, ResultMsg.UPDATE_SUCCESS);
    }

    /** 查询部门 */
    async findById(id: string): Promise<Result<DeptDetailRawVo>> {
        const item = await this.departRepository
            .createQueryBuilder("dept")
            .leftJoin("sys_dept_type", "dept_type", "dept.type = dept_type.id")
            .select(DETAIL_SELECT)
            .addSelect(COLUMN_IS_LEAF)
            .where("dept.id = :id", { id })
            .getRawOne();
        const deptVo = plainToInstance(DeptDetailRawVo, item, { excludeExtraneousValues: true });
        return Result.success(deptVo);
    }

    /** 获取子部门 */
    async listChildren(parentId: string): Promise<Result<DeptDetailRawVo[]>> {
        const departs = await this.departRepository
            .createQueryBuilder("dept")
            .leftJoin("sys_dept_type", "dept_type", "dept.type = dept_type.id")
            .select(DETAIL_SELECT)
            .addSelect(COLUMN_IS_LEAF)
            .where("dept.parent_id = :parentId", { parentId })
            .getRawMany();
        const children = plainToInstance(DeptDetailRawVo, departs, { excludeExtraneousValues: true });
        return Result.success(children);
    }

    /** 获取指定部门的所有部门, 并返回树形结构 */
    async listTree(code: string | null) {
        const departs = await this.departRepository
            .createQueryBuilder("dept")
            .leftJoin("sys_dept_type", "dept_type", "dept.type = dept_type.id")
            .select(DETAIL_SELECT)
            .where("code like :cond", { cond: code ? `${code}%` : "%" })
            .getRawMany();

        // 转换为树形结构
        const departVos = plainToInstance(DeptDetailRawVo, departs, { excludeExtraneousValues: true });
        const map = new Map<string, DeptDetailRawVo>();
        const tree: DeptDetailRawVo[] = [];
        departVos.forEach((item) => {
            map.set(item.id, item);
        });
        departVos.forEach((item) => {
            if (item.parentId && map.has(item.parentId)) {
                const parent = map.get(item.parentId);
                if (!parent) return;
                parent.children = parent.children || [];
                parent.children.push(item);
            } else {
                tree.push(item);
            }
        });

        return Result.success(tree);
    }

    /** 根据名称查询查询某部门下面的部门 */
    async listByName(data: QueryDeptByNameDto): Promise<Result<DeptDetailRawVo[]>> {
        const { name, code, maxLevel } = data;
        const queryBuilder = this.departRepository.createQueryBuilder("dept");
        queryBuilder
            .leftJoin("sys_dept_type", "dept_type", "dept.type = dept_type.id")
            .select(DETAIL_SELECT)
            .where("dept.name LIKE :name", { name: `%${name}%` })
            .andWhere("dept.code LIKE :code", { code: `${code}%` });
        if (maxLevel) {
            queryBuilder.andWhere("dept.level <= :maxLevel", { maxLevel });
        }
        const items = await queryBuilder.getRawMany();
        const departVos = plainToInstance(DeptDetailRawVo, items, { excludeExtraneousValues: true });
        return Result.success(departVos);
    }
}

/**
 * 获取新部门编码, 优先使用通层级下被软删除的部门编码, 否则根据最大序号生成新的编码
 */
const getNewDepartCode = async (
    repository: Repository<SysDept>,
    data: CreateDeptDto & { parentCode: string | undefined },
) => {
    const delDept = await repository
        .createQueryBuilder("dept")
        .select("dept.code code, dept.id id")
        .where("dept.level = :level", { level: data.level }) // 同级
        .andWhere("dept.code like :reg", { reg: `${data.parentCode}%` }) // 以父节点 code 开头
        .andWhere("dept.deleted_at is not null") // 仅查询被软删除的数据
        .withDeleted()
        .getRawOne();

    let code = delDept ? delDept.code : "";

    if (code) {
        await repository.update(delDept?.id, { code: `U${delDept.code}` }); // 更新前缀 U, 表示被在删除 code 列表中被使用过
    } else {
        const parentCode = data.parentCode ?? "";
        // 没有被删除的可用 code，则生成新的 code
        const query: any = await repository
            .createQueryBuilder("dept")
            .select("max(dept.code)", "max")
            .where("dept.level = :level", { level: data.level }) // 同级
            .andWhere("dept.code ~ :reg", { reg: "^[0-9]" }) // 仅数字开头
            .andWhere("dept.code like :parentCode", { parentCode: `${parentCode}%` }) // 以父节点 code 开头
            .getRawOne();

        // 切割 code，最后一段 +1 作为新的 code, 同时高位补0
        const lastCode = query.max ? parseInt(query.max.split(DEPT_CODE_SPLIT_SYMBOL).pop(), 10) : 1;
        const newCode = (lastCode + 1).toString().padStart(3, "0");
        code = parentCode ? `${parentCode}${DEPT_CODE_SPLIT_SYMBOL}${newCode}` : newCode;
    }

    return code;
};
